﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Controller.cs" company="" />
// <summary>
//   The controller of the Xompare application.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Xompare
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Globalization;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using FormsUtilities;

    using Utilities;

    using Xompare.Configuration;
    using Xompare.Helpers;

    /// <summary>
    /// The controller of the Xompare application.
    /// </summary>
    public static class Controller
    {
        /// <summary>
        /// The sync object allowing only one configuration save at a time.
        /// </summary>
        private static readonly object s_saveConfigurationSync = new object();

        /// <summary>
        /// The timeout to allow a configuration save before waiving it with an error.
        /// </summary>
        private static readonly TimeSpan s_configurationSaveTimeout = TimeSpan.FromSeconds(30);

        static Controller()
        {
            RefreshConfiguration();
        }

        /// <summary>
        /// Gets the maintained configuration section of the application.
        /// </summary>
        public static XompareConfigSection ConfigInfo { get; private set; }

        /// <summary>
        /// Gets the enumeration of known extension patterns and behaviors.
        /// </summary>
        public static IEnumerable<KnownExtensionConfigElement> KnownExtensions { get; private set; }

        /// <summary>
        /// Gets or sets the main form of the application.
        /// </summary>
        public static MdiContainer AppMainForm { get; set; }

        #region CLI parser related

        /// <summary>
        /// Gets or sets the arguments recieved from the CLI.
        /// </summary>
        public static string[] CliArguments { get; set; }

        /// <summary>
        /// Gets or sets the CLI help message for CLI syntax.
        /// </summary>
        public static string CliHelpMessage { get; set; }

        #endregion

        #region GUI related

        /// <summary>
        /// Create a new tab with XML files comparator.
        /// </summary>
        /// <param name="getOriginalFilePath">A func to get the original XML file path.</param>
        /// <param name="getTargetFilePath">A func to get the target XML file path.</param>
        public static void CreateFileDiffTab(Func<NameAndLabel> getOriginalFilePath, Func<NameAndLabel> getTargetFilePath)
        {
            AppMainForm.CreateFileDiffTab(getOriginalFilePath, getTargetFilePath);
        }

        #endregion

        /// <summary>
        /// Gets or sets whether the Ctrl key is currently pressed.
        /// </summary>
        public static bool IsControlPressed { get; set; }

        /// <summary>
        /// The the recommended default key for a given pattern. Consults also the configuration information.
        /// </summary>
        /// <param name="pattern">A specific file pattern (as appearing in the configuration) or 'null'.</param>
        /// <returns>The recommended default key for the comparison.</returns>
        public static string GetDefaultKey(string pattern = null)
        {
            var requiredExtension = KnownExtensions.FirstOrDefault(extension => extension.Pattern.Equals(pattern));

            return requiredExtension != null && !string.IsNullOrEmpty(requiredExtension.DefaultKey)
                ? requiredExtension.DefaultKey
                : ConfigInfo.DefaultKey;
        }

        /// <summary>
        /// Refresh the configuration information.
        /// </summary>
        public static void RefreshConfiguration()
        {
            ConfigInfo = new AppConfigManager<XompareConfigSection>(XompareConfigSection.SectionName).Configuration;

            KnownExtensions = ConfigInfo.KnownExtensions.Cast<KnownExtensionConfigElement>();
        }

        /// <summary>
        /// Save the current application configuration.
        /// If cannot save within a timeout - issues an error and leaves without saving.
        /// </summary>
        public static void SaveConfiguration()
        {
            System.Configuration.Configuration applicationConfiguration = null;

            try
            {
                lock (s_saveConfigurationSync)
                {
                    Task.Factory.StartNew(
                        () =>
                        {
                            applicationConfiguration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                            var appConfigSection = (XompareConfigSection)applicationConfiguration.GetSection(XompareConfigSection.SectionName);

                            appConfigSection.GuiDefinitionConfig = ConfigInfo.GuiDefinitionConfig;
                            applicationConfiguration.Save(ConfigurationSaveMode.Minimal, false);
                        }).Wait(s_configurationSaveTimeout);
                }
            }
            catch (AggregateException taskException)
            {
                string errorMessage;
                if (applicationConfiguration == null)
                {
                    errorMessage = string.Format(
                        CultureInfo.InvariantCulture,
                        "Could not save configuration object within {0}. Exception error: {1}",
                        s_configurationSaveTimeout,
                        taskException.Message);

                }
                else
                {
                    errorMessage = string.Format(
                        CultureInfo.InvariantCulture,
                        "Could not save configuration object to {0} within {1}. Exception error: {2}",
                        applicationConfiguration.FilePath,
                        s_configurationSaveTimeout,
                        taskException.Message);
                }

               LogUi.LogFatal(errorMessage);
            }
        }

        /// <summary>
        /// Set the title of the application.
        /// </summary>
        /// <param name="newTitlePostFix">The postfix to set to the application title.</param>
        public static void SetApplicationTitle(string newTitlePostFix)
        {
            AppMainForm.Text =
                string.IsNullOrEmpty(newTitlePostFix)
                    ? Application.ProductName
                    : string.Join(" - ", Application.ProductName, newTitlePostFix);
        }
    }
}
